using System;
using System.Text;
using System.Text.RegularExpressions;
using Meebey.SmartIrc4net;

namespace DiceBot.Answerers
{
	internal class Dicer : IAnswerer
	{
		private Random _dice;
		private const string addString = "add";
		private const string diceString = "dice";
		private const string numberOfDiceString = "number";
		private const string numberOfRollsString = "numberOfRolls";
		private const string minCritString = "minCrit";
		private const string maxCritString = "maxCrit";
		private readonly string mIrcColorCode = char.ConvertFromUtf32(0x03);

		private Regex _diceRule = new Regex(
			string.Format(
				@"^((?<{0}>\d+)#)?(?<{1}>\d+)?d(?<{2}>\d+)(?<{3}>(\+|\-)\d+)?(\[(?<{4}>\d+)\-(?<{5}>\d+)\])?$",
				new object[]
					{
						numberOfRollsString, numberOfDiceString, diceString, addString,
						minCritString, maxCritString
					}),
			RegexOptions.Compiled);

		public Dicer()
		{
			Initialize();
		}

		#region IAnswerer Members

		public string Answer(Data ircData)
		{
			StringBuilder result = new StringBuilder();

			foreach (string word in ircData.MessageEx)
			{
				if (CheckDice(result, word))
					result.Append(";");
			}

			if (result.Length == 0)
				return null;

			result.Remove(result.Length - 1, 1);
			result.Append(".");

			result.Insert(0, string.Format("{0},", ircData.Nick));

			return result.ToString();
		}

		private bool CheckDice(StringBuilder result, string word)
		{
			Match match = _diceRule.Match(word);
			if (match.Success)
			{
				Group numberOfRollsGroup = match.Groups[numberOfRollsString];
				Group numberOfDiceGroup = match.Groups[numberOfDiceString];
				Group diceGroup = match.Groups[diceString];
				Group addGroup = match.Groups[addString];
				Group minCritGroup = match.Groups[minCritString];
				Group maxCritGroup = match.Groups[maxCritString];
				int dice;
				int numberOfRolls;
				int numberOfDice;
				int add;
				int minCrit;
				int maxCrit;

				if (numberOfRollsGroup.Success)
					int.TryParse(numberOfRollsGroup.Value, out numberOfRolls);
				else
					numberOfRolls = 1;

				if (numberOfDiceGroup.Success)
					int.TryParse(numberOfDiceGroup.Value, out numberOfDice);
				else
					numberOfDice = 1;

				if (diceGroup.Success)
					int.TryParse(diceGroup.Value, out dice);
				else
					return false;

				if (addGroup.Success)
					int.TryParse(addGroup.Value, out add);
				else
					add = 0;

				if (minCritGroup.Success)
					int.TryParse(minCritGroup.Value, out minCrit);
				else
					minCrit = dice;

				if (maxCritGroup.Success)
					int.TryParse(maxCritGroup.Value, out maxCrit);
				else
					maxCrit = dice;

				if (numberOfDice <= 0 || numberOfRolls <= 0 || dice <= 0 ||
				    maxCrit <= 0 || minCrit <= 0 || minCrit > dice || maxCrit > dice)
					return false;

				result.AppendFormat(" ({0})", word);
				for (int roll = 0; roll < numberOfRolls; roll++)
				{
					int rollResult = 0;
					for (int diceIndex = 0; diceIndex < numberOfDice; diceIndex++)
					{
						rollResult += _dice.Next(1, dice + 1);
					}

					if (rollResult >= minCrit && rollResult <= maxCrit)
					{
						result.AppendFormat("{1}3 {0}{1}", rollResult + add, mIrcColorCode);
					}
					else
					{
						result.AppendFormat(" {0}", rollResult + add);
					}
				}

				return true;
			}

			return false;
		}

		public void Initialize()
		{
			_dice = new Random();
		}

		#endregion
	}
}